/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "glheaders.h"
#include "view.h"
#include "controller.h"
#include "mouseUtils.h"
#include "renderUtils.h"
#include "ui/uiUtils.h"
#include "graph.h"

#include <math.h>
#include <assert.h>

#include <algorithm>
#include <sstream>


View::View(const char *type, const char*name):Clickable(), PropertyBag(type, name) {
    enableDefaultSelection = true;
    bTransparent = false;
    visible = true;
    setPropertyBool("View.visible", true);
    setPropertyBool("View.transparent", false);
    setPropertyBool("View.collapsed", false);
    setPropertyBool("View.locked", false);
    setPropertyStringList("View.Selection colormap", "_Virtual$selection.colormap", "colormap");

    selectionWidth = selectionHeight = 0;
    controller = 0;
    validGeometryCache = false;
    setMargins(0,0,0,0);
    zoom = 1.0;
    panx = pany = 0.0;
    zoomable = true;
    clickStatus = 0;
    currentFont = "Verdana";
    currentFontSize = 12;
    currentFontStyle = "normal";
    graph = 0;

    // Font settings
    setPropertyInt("View.Font Size",currentFontSize);
    setPropertyStringList("View.Font", currentFont,"font");
		setPropertyInt("View.width", 0);
		setPropertyInt("View.height", 0);

}

View::~View() {
    for (vector<Clickable*>::iterator it = viewClickables.begin(); it != viewClickables.end(); ++it) {
        delete (*it);
    }
    for (vector<Clickable*>::iterator it = windowClickables.begin(); it != windowClickables.end(); ++it) {
        delete (*it);
    }
}

// To do: tell the view which property [DONE]
void View::update(string) {
}

bool View::isVisible() {
    return getPropertyBool("View.visible");
}

bool View::encloses(float px, float py) { 
    GLint V[4];
    glGetIntegerv(GL_VIEWPORT, V);

    float sx = 50.0/V[2];
    float sy = 50.0/V[3];
    bool val = (px>x && px<x+w && py>y-h && py<y); hovered = val;

    dragMode = NONE;
    if(val) {
        dragMode = (px>x+w-sx && py<y-h+sy)? RESIZE:(py>y-titleHeight)? MOVE : DRAG;
    }

    return val;
}

void View::drag(float px, float py, float dx, float dy, int modifiers) { 
    bool bVisible = getPropertyBool("View.visible");
    if(!bVisible) return;

    GLint V[4];
    glGetIntegerv(GL_VIEWPORT, V);
    float dim = fmax(V[3],V[2]);
    float aspect = (float) V[2]/(float) V[3];
    float minw = 200.0/dim;
    float minh = 200.0/dim;
    bool locked = getPropertyBool("View.locked");
    float rx, ry;
    toWindow(px, py, rx, ry);
    for(unsigned int k=0;k<windowClickables.size();k++) {
        if(windowClickables[k]->isSelected()) {
            windowClickables[k]->drag(rx, ry, dx,dy, modifiers);
            return;
        }
    }
    float rdx, rdy;
    toViewDisplacement(dx,dy, rdx, rdy);

    if(zoomable) {
			if(modifiers & NZ_ACTIVE_SHIFT) {
				panx += rdx;
				pany += rdy;
				//dragMode = PAN;
				return;
			} else if(modifiers & NZ_ACTIVE_CTRL) {
				float d = 5.0*dy;
				zoom *=exp(d);
				//dragMode = ZOOM;
				return;
			} 
    }
    toView(px, py, rx, ry);
    for(unsigned int k=0;k<viewClickables.size();k++) {
        if(viewClickables[k]->isSelected()) {
            viewClickables[k]->drag(rx, ry, rdx,rdy, modifiers);
            return;
        }
    }

    if(!locked) {
        if(dragMode==MOVE) {
            x+=dx; y+=dy;
            if(x<-aspect) x = -aspect;
            if(x>aspect-w) x = aspect - w;
            if(y<-1+h) y = -1+h;
            if(y>1.0) y = 1.0;
        }
        if(dragMode==RESIZE) {
            w+=dx; h-=dy;
            w = fmax(minw, w);
            h = fmax(minh, h);
            if(x+w>aspect) w = aspect - x;
            if(y-h<-1.0) h = y+1.0;
        }
    }
    performingSelection = false;
    if(dragMode==DRAG) {
			float absdx, absdy;
			float viewdx, viewdy;
			absdx = dx + x + margins[0];
			absdy = dy + y-h + margins[2];
			toViewDisplacement(dx, dy, viewdx, viewdy);
			selectionWidth+= viewdx ;
			selectionHeight+=viewdy ;
			performingSelection = true;
    }
}
void View::mouseMove(float px, float py, float dx, float dy, int modifiers) { 
    hoveredTitle = (py>y-titleHeight && py<y && px>x && px<x+w);
    float rx, ry;
    toWindow(px, py, rx, ry);
    for(unsigned int k=0;k<windowClickables.size();k++) {
        if(windowClickables[k]->encloses(rx,ry)) {
            windowClickables[k]->mouseMove(rx, ry, dx,dy,modifiers);
            windowClickables[k]->hovered = true;
        } else {
            windowClickables[k]->hovered = false;
        }
    }
    float rdx, rdy;
    toViewDisplacement(dx,dy, rdx, rdy);
    toView(px, py, rx, ry);
    for(unsigned int k=0;k<viewClickables.size();k++) {
        if(viewClickables[k]->encloses(rx,ry)) {
            viewClickables[k]->mouseMove(rx, ry, rdx,rdy,modifiers);
            viewClickables[k]->hovered = true;
        } else {
            viewClickables[k]->hovered = false;
        }
    }
}
void View::release(float px, float py, int modifiers) { 
    if(dragMode==DRAG) {
        selectionWidth = selectionHeight = 0;
    }
    dragMode = NONE;

    float rx, ry;
    toWindow(px, py, rx, ry);
    for(unsigned int k=0;k<windowClickables.size();k++) {
        if(windowClickables[k]->isSelected()) {
            windowClickables[k]->release(rx, ry, modifiers);
            return;
        }
    }
    toView(px, py, rx, ry);
    for(unsigned int k=0;k<viewClickables.size();k++) {
        if(viewClickables[k]->isSelected()) {
            viewClickables[k]->release(rx, ry, modifiers);
            return;
        }
    }
}

void View::getSelectionColor(int selected, float *rgb) {
    string selectionColormap = PROPERTY(getPropertyString("View.Selection colormap"));
    controller->getColor(selectionColormap, selected, rgb);
}


class SelectionWidget: public Clickable {
    View *view;
    float selx, sely;
    float pixelWidth, pixelHeight;
    int selectionx;
    int selectiony;
public:
    Clickable *left, *right;
    SelectionWidget(View *v) {
        this->view = v;
        selectionx = 1;
        pixelWidth = 45.0;
        pixelHeight = 15.0;
        view->setSelectionMarker(selectionx);
    }
    virtual void draw() {
        float rgb[3];

        float dx, dy;
        view->getPixelWindow(dx,dy);
        w = pixelWidth*dx;
        h = pixelHeight*dy;

        x = view->x + view->w - 1.5*15.0*dx - 1.5*w;
        y = view->y - view->titleHeight + 0.5*(view->titleHeight - h);

        view->getSelectionColor(selectionx, rgb);

        glPushMatrix();
        glTranslatef(x + 0.5*w + dx , y + 0.5*h  - dy,0);
        glColor4f(1,1,1,0.9);
        elongatedCircle(w, h, true);
        glPopMatrix();

        glPushMatrix();
        glTranslatef(x + 0.5*w , y + 0.5*h ,0);

        //glColor4f(0.8,0.8,0.8,1.0);

        //elongatedCircle(w, h, false);
        glColor4f(0.0,0.0,0.0,0.2);
        elongatedCircle(w, h, true);

        glColor4f(0,0,0,0.5);
        glBegin(GL_TRIANGLES);
        {
            glVertex2f(-0.4*w , 0.0);
            glVertex2f(-0.25*w , -0.2*h);
            glVertex2f(-0.25*w , 0.2*h);

            glVertex2f(0.4*w , 0.0);
            glVertex2f(0.25*w , -0.2*h);
            glVertex2f(0.25*w , 0.2*h);
        }
        glEnd();

        glColor4f(rgb[0], rgb[1], rgb[2], 1);
        glBegin(GL_QUADS);
        {
            glVertex2f(-0.2*w, -0.3*h);
            glVertex2f(-0.0*w, -0.3*h);
            glVertex2f(-0.0*w, +0.3*h);
            glVertex2f(-0.2*w, +0.3*h);
        }
        glEnd();
        glColor4f(0.5,0.5,0.5,0.7);
        glBegin(GL_LINE_LOOP);
        {
            glVertex2f(-0.2*w, -0.3*h);
            glVertex2f(-0.0*w, -0.3*h);
            glVertex2f(-0.0*w, +0.3*h);
            glVertex2f(-0.2*w, +0.3*h);
        }
        glEnd();

        glColor4f(0,0,0,0.5);

        glBegin(GL_LINES);
        {
            glVertex2f(+0.05*w, -0.1*h);
            glVertex2f(+0.15*w, +0.2*h);

            glVertex2f(+0.15*w, -0.1*h);
            glVertex2f(+0.05*w, +0.2*h);
        }
        glEnd();
        char strSel[30];
        sprintf(strSel, "%d\n", selectionx);
        //drawText(-0.2*w, -0.2*h, strSel);
        glPopMatrix();

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glColor4f(0,0,0,1);

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    virtual void click(float px, float /*py*/, int /*modifiers*/) {
        assert(view);
        float dx, dy;

        view->getPixelWindow(dx,dy);
        if(px - x < 0.33*w) {
            selectionx--;
            if(selectionx<1) selectionx=1;
        }
        if(px - x > 0.67*w) {
            selectionx++;
        }
        if(px - x > 0.55*w && px-x<0.7*w) {
            view->clearSelection(selectionx);
        }
        if(px - x > 0.3*w && px-x<0.5*w) {
            float rgb[3];
            bool valid = queryColor(rgb);
            string selectionColormap = view->PROPERTY(view->getPropertyString("View.Selection colormap"));
            if(valid) {
                view->controller->getColormap(selectionColormap)->setColor(selectionx, rgb[0], rgb[1], rgb[2]);
                view->controller->colorUpdated();
            }
        }
        view->setSelectionMarker(selectionx);
    }
};


void View::addSelectionWidget() {
    SelectionWidget *c = new SelectionWidget(this);
    addWindowClickable(c);
}

class CloseButton:public Clickable {
    View *view;
public:
    CloseButton(View *view): Clickable() {
        this->view = view;
    }
    virtual ~CloseButton() {
    }
    void click(float /*px*/, float /*py*/, int /*modifiers*/) {
        assert(view);
        view->setPropertyBool("View.visible", false);
    }
    void draw() {
        assert(view);

        glLineWidth(1);
        float dx, dy;
        view->getPixelWindow(dx,dy);
        w = 12.0*dx;
        h = 12.0*dy;
        float r = 0.5 * fmin(w,h);
        x = view->x + view->w - 1.5*w;
        y = view->y - view->titleHeight + 0.5*(view->titleHeight - h);


        glPushMatrix();
        glTranslatef(x + r+dx , y + r - dy,0);
        glColor4f(1,1,1,0.9);
        // circle(r, true);
        glPopMatrix();
        glPushMatrix();
        glTranslatef(x + r , y + r,0);

        glPushMatrix();
        {
            glTranslatef(0.5*dx, -1.05*dy,0);
            fuzzyCircle(0, 1.0, r+3.0*dx, 0.0, 0,0,0);
        }
        glPopMatrix();

        glColor4f(1.0,0.0,0.0,0.8);
        circle(r, false);
        glColor4f(0.0,0.0,0.0,0.5);
        circle(r, true);


        glPopMatrix();

    }

};

class TransparentButton:public Clickable {
    View *view;
public:
    TransparentButton(View *view): Clickable() {
        this->view = view;
    }
    void click(float /*px*/, float /*py*/, int /*modifiers*/) {
        assert(view);
        bool transparent =  view->getPropertyBool("View.transparent");
        view->setPropertyBool("View.transparent", !transparent);
    }
    void draw() {
        assert(view);

        float dx, dy;
        view->getPixelWindow(dx,dy);
        w = 12.0*dx;
        h = 12.0*dy;
        float r = 0.5 * fmax(w,h);
        x = view->x + view->w - 1.5*w - 1.5*w;
        y = view->y - view->titleHeight + 0.5*(view->titleHeight - h);

        glLineWidth(1);

        glPushMatrix();
        glTranslatef(x + r+dx , y + r - dy,0);
        glColor4f(1,1,1,0.9);
        //circle(r, true);
        glPopMatrix();
        glPushMatrix();
        glTranslatef(x + r , y + r,0);

        glPushMatrix();
        {
            glTranslatef(0.5*dx, -1.05*dy,0);
            fuzzyCircle(0, 1.0, r+3.0*dx, 0.0, 0,0,0);
        }
        glPopMatrix();

        glColor4f(0.9,0.9,0.9,0.8);
        circle(r, false);
        glColor4f(0.0,0.0,0.0,0.5);
        circle(r, true);

        glPopMatrix();

    }

};

void View::addCloseButton() {
    Clickable * c = new CloseButton(this);
    addWindowClickable(c);
}
void View::addTransparentButton() {
    Clickable * c = new TransparentButton(this);
    addWindowClickable(c);
}
void View::addExpandButton() {
}
void View::addCustomWidget(Clickable *) {
}

void View::setSelectionMarker(int sel) {
    selectionMarker = sel;
}

void View::getPixel(float &x, float &y) {
    //GLint V[4];
    //glGetIntegerv(GL_VIEWPORT, V);
    float dim = fmax(V[3],V[2]);
    dim = V[3];
    x = 2.0*(1.0/(float) dim)/(w);
    dim = V[3];
    y = 2.0*(1.0/(float) dim)/(h-titleHeight);
}

void View::getPixelWindow(float &x, float &y) {
    float dim = fmax(V[3],V[2]);
    dim = V[3];
    x = 2.0*(1.0/(float) dim);
    dim = V[3];
    y = 2.0*(1.0/(float) dim);
}

void View::drawTitle() {
    GLint V[4];
    glGetIntegerv(GL_VIEWPORT, V);
    titleHeight = 45.0/(float) V[3];

    float dx = 1.0/(float) V[2];
    float dy = 1.0/(float) V[3];

    glBegin(GL_QUADS);
    {
        glColor4f(0.9,0.9,0.9,1.0);
        glVertex2f(x  ,y);
        glVertex2f(x+w,y);
        glColor4f(0.7,0.7,0.7,1.0);
        glVertex2f(x+w,y-titleHeight);
        glVertex2f(x  ,y-titleHeight);
    }
    glEnd();

    /*
  glColor4f(1,1,1,0.9);
  drawText(x+10*dx, y - titleHeight + 10*dy, (char*) getName().c_str());
  glColor4f(0,0,0,0.9);
  drawText(x+10*dx-dx, y - titleHeight + 10*dy+dy, (char*) getName().c_str()); 
  */
    glColor4f(0,0,0,0.9);
    drawTextBold(x+10*dx-dx, y - titleHeight + 10*dy+dy, (char*) getName().c_str());

    glBegin(GL_LINE_STRIP);
    {
        glColor4f(1,1,1,0.9);
        glVertex2f(x,y-titleHeight);
        glVertex2f(x,y);
        glVertex2f(x+w,y);
        glColor4f(0.2,0.2,0.2,0.9);
        glVertex2f(x+w,y-titleHeight);
        glVertex2f(x,y-titleHeight);
    }
    glEnd();

    if(!isSelected()) {
        glColor4f(1,1,1,0.6);
        glRectf(x,y-titleHeight, x+w, y);
    }
}

/**
 * Deprecated: going the way of the dodo
 * Use toViewDisplacement instead.
 */
void View::toRelativeDisplacement(float dx, float dy, float &rdx, float &rdy) {
    toRelative(dx+x+margins[0], dy+y-h+margins[2], rdx, rdy);
}

/**
 * Deprecated: going the way of the dodo
 * Use toView instead.
 */
void View::toRelative(float wx, float wy, float &rx, float &ry) {
    rx = (wx - x - margins[0])/(w - (margins[0]+margins[1]));
    ry = (wy - (y-h) - margins[2])/(h-titleHeight - (margins[2]+margins[3]));
}


void View::toWindow(float wx, float wy, float &rx, float &ry) {
    rx = (wx - x - margins[0]);
    ry = (wy - (y-h) - margins[2]);
}
void View::toWindowDisplacement(float wx, float wy, float &rx, float &ry) {
    rx = wx;
    ry = wy;
}
void View::toView(float wx, float wy, float &rx, float &ry) {
    float x, y;
    toWindow(wx, wy, x, y);
    float width = (w - (margins[0]+margins[1]));
    float height = (h-titleHeight - (margins[2]+margins[3]));
    rx = (x/width - 0.5)/zoom - panx + 0.5;
    ry = (y/height - 0.5)/zoom - pany + 0.5;
}
void View::toViewDisplacement(float wx, float wy, float &rx, float &ry) {
    rx = wx/(w - (margins[0]+margins[1]));
    ry = wy/(h-titleHeight - (margins[2]+margins[3]));
    rx = rx/zoom;
    ry = ry/zoom;
}

bool View::inSelection(float x, float y) {
    float x1 = fmin(selectionX, selectionX+selectionWidth);
    float x2 = fmax(selectionX, selectionX+selectionWidth);
    float y1 = fmin(selectionY, selectionY+selectionHeight);
    float y2 = fmax(selectionY, selectionY+selectionHeight);

    return(x>x1 && x<x2 && y>y1 && y<y2);
}


bool View::intersectsSelection(float x1, float y1, float x2, float y2) {
    float sx1 = fmin(selectionX, selectionX+selectionWidth);
    float sx2 = fmax(selectionX, selectionX+selectionWidth);
    float sy1 = fmin(selectionY, selectionY+selectionHeight);
    float sy2 = fmax(selectionY, selectionY+selectionHeight);
    if(inSelection(x1,y1)) return true;
    if(inSelection(x2,y2)) return true;

    bool b;
    b = lines_intersect(x1,y1,x2,y2, sx1, sy1, sx1, sy2);
    if(b) return true;
    b = lines_intersect(x1,y1,x2,y2, sx1, sy2, sx2, sy2);
    if(b) return true;
    b = lines_intersect(x1,y1,x2,y2, sx2, sy2, sx2, sy1);
    if(b) return true;
    b = lines_intersect(x1,y1,x2,y2, sx2, sy1, sx1, sy1);
    if(b) return true;

    return false;
}

bool View::intersectLineBox(float x1, float y1, float x2, float y2, float boxX, float boxY, float boxW, float boxH) {
    float sx1 = fmin( boxX,  boxX+ boxW);
    float sx2 = fmax( boxX,  boxX+ boxW);
    float sy1 = fmin( boxY,  boxY+ boxH);
    float sy2 = fmax( boxY,  boxY+ boxH);
    if(inSelection(x1,y1)) return true;
    if(inSelection(x2,y2)) return true;

    bool b;
    b = lines_intersect(x1,y1,x2,y2, sx1, sy1, sx1, sy2);
    if(b) return true;
    b = lines_intersect(x1,y1,x2,y2, sx1, sy2, sx2, sy2);
    if(b) return true;
    b = lines_intersect(x1,y1,x2,y2, sx2, sy2, sx2, sy1);
    if(b) return true;
    b = lines_intersect(x1,y1,x2,y2, sx2, sy1, sx1, sy1);
    if(b) return true;

    return false;
}

void View::click(float px, float py, int modifiers) {
    bool bVisible = getPropertyBool("View.visible");
    if(!bVisible) return;

    float rx, ry;
    toWindow(px, py, rx, ry);
    clickStatus = 0;
    for(unsigned int k=0;k<windowClickables.size();k++) {
        if(windowClickables[k]->encloses(px, py)) {
            clickStatus = 1;
            windowClickables[k]->click(px,py,modifiers);
            windowClickables[k]->setSelected(true);
        } else {
            windowClickables[k]->setSelected(false);
        }
    }
    selectionWidth = selectionHeight = 0;
    toView(px, py, selectionX, selectionY);
    for(unsigned int k=0;k<viewClickables.size();k++) {
        if(viewClickables[k]->encloses(selectionX, selectionY)) {
            clickStatus = 1;
            viewClickables[k]->setSelected(true);
            viewClickables[k]->click(selectionX, selectionY, modifiers);
        } else {
            viewClickables[k]->setSelected(false);
        }
    }
}

void View::setMargins(float l, float r, float t, float b) {
    this->marginTop = t;
    this->marginBottom = b;
    this->marginLeft = l;
    this->marginRight = r;
}

void View::updatePixelScale() {
    float pixx0, pixy0;
    getPixelWindow(pixx0, pixy0);
    toViewDisplacement(pixx0, pixy0, pixx, pixy);
    margins[0] = marginLeft/V[2];
    margins[1] = marginRight/V[2];
    margins[2] = marginBottom/V[3];
    margins[3] = marginTop/V[3];
}

void View::draw() {
    bool bVisible = getPropertyBool("View.visible");
    if(!bVisible) return;
	
		updateFont();

    glGetIntegerv(GL_VIEWPORT, V);
    updatePixelScale();
    titleHeight = 44.0/(float) V[3];

    float dx = 1.0/(float) V[2];
    float dy = 1.0/(float) V[3];

    float aspect = (float) V[2]/(float) V[3];
    GLdouble vx, vy, vw, vh, vz;
		GLdouble l,r,t,b;
		GLdouble model[16], proj[16];
		glGetDoublev(GL_MODELVIEW_MATRIX, model);
		glGetDoublev(GL_PROJECTION_MATRIX, proj);
		l=(x  );//+V[2]/2;
		r=(x+w);//+V[2]/2;
		b=y-h;
		t=y-titleHeight;
//		gluUnProject(x, y, 0,  model,  proj,  V,  &vx,  &vy,  &vz);
//		gluUnProject(x+w, y+h, 0,  model,  proj,  V,  &vw,  &vh,  &vz);
		gluProject(l, b, 0,model,proj,V,&vx,  &vy,  &vz);
		gluProject(r, t, 0,model,proj,V,&vw,  &vh,  &vz);
//    vx = (int) (0.5*(x/aspect+1.0)/dx);
//    vy = (int) (0.5*(y+1)/dy);
//    vw = (int) (0.5*(w/aspect)/dx);
//    vh = (int) (0.5*(h)/dy);
    //print("%f %f (asp = %f) --> Viewport %d %d %d %d\n", x, y, aspect, vx, vy, vw, vh);
    glScissor(vx,vy,vw-vx+1,vh-vy);
//    glScissor(0,0,V[2], V[3]);
		//x = y = 0;
    glHint(GL_POINT_SMOOTH, GL_NICEST);
    glHint(GL_LINE_SMOOTH, GL_NICEST);
    glHint(GL_POLYGON_SMOOTH, GL_NICEST);

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_SMOOTH);
    //glEnable(GL_POLYGON_SMOOTH); // Causes white lines along edges of OpenGL internal primitives
    glDisable(GL_POLYGON_SMOOTH); 

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    bTransparent = (getPropertyBool("View.transparent"));

	// cc. We need a general way to set up color
	// one possibility is to create a color manager and define a mapping
	// similar to Qt's palettes
	this->fColor_back = 1.0;  //back: white 1.0
	this->fColor_front = 0.0; //fore: black

	this->fColor_backTrans = fColor_back-0.2; //light gray 0.8
	this->fColor_backTrans2 = fColor_back-0.8; //dark gray 0.2


    if(!bTransparent) {
        // Shadow
        shadowRectangle(x, y-h, w, h, 20.0*dx, 20.0*dy);

        drawTitle();

        drawBackground();

        for(unsigned int k=0;k<windowClickables.size();k++) {
            windowClickables[k]->draw();
        }
    } else if(hoveredTitle) {
        drawTitle();
        for(unsigned int k=0;k<windowClickables.size();k++) {
            windowClickables[k]->draw();
        }
    }

    glEnable(GL_SCISSOR_TEST);

    // Work area

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    {
        glTranslatef(x, y-h, 0);
        glTranslatef(margins[0], margins[2], 0);
        //glScalef(w - (margins[0]+margins[1]), (h-titleHeight) - (margins[2]+margins[3]),1.0);
        glScalef(w - 1.0*(margins[0]+margins[1]), (h-titleHeight) - 1.0*(margins[2]+margins[3]),1.0);

        glPushMatrix();
        glTranslatef(0.5, 0.5, 0);
        glScalef(zoom,zoom,1.0);
        glTranslatef(panx, pany, 0);
        glTranslatef(-0.5, -0.5, 0);

        render();

        // Selection
        if(enableDefaultSelection) {
            float rgb[3];
            getSelectionColor(selectionMarker, rgb);
            glColor4f(rgb[0], rgb[1], rgb[2], 0.1);
            glRectf(selectionX, selectionY, selectionX + selectionWidth, selectionY + selectionHeight);
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glColor4f(rgb[0],rgb[1],rgb[2], 0.5);
            glRectf(selectionX, selectionY, selectionX + selectionWidth, selectionY + selectionHeight);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        }

        glPopMatrix();

        for(unsigned int k=0;k<viewClickables.size();k++) {
            viewClickables[k]->draw();
        }

    }
    glPopMatrix();

    glDisable(GL_SCISSOR_TEST);
    glScissor(0,0,V[2], V[3]);
    if(!bTransparent || hoveredTitle) {
        // Decoration
        float sx = 25.0/(float) V[3];
        float sy = 25.0/(float) V[3];

        glBegin(GL_LINES);
        {
            // Resize lines
            glColor4f(0,0,0,0.6);
            glVertex2f(x+w-sx, y-h);
            glVertex2f(x+w, y-h+sy);

            glColor4f(1,1,1,0.9);
            glVertex2f(x+w-sx+dx, y-h);
            glVertex2f(x+w   +dx, y-h+sy);

            glEnd();
        }
    }

    //semiSuperQuadric(0.25, 0.05, 0.05, 0.25, 0.6, true);
    glPopMatrix();
    //  glViewport(V[0], V[1], V[2], V[3]);
}

void View::drawBackground() {
    /*
  glBegin(GL_LINES);
  glColor3f(1,0,0);
  glVertex2f(0,0);
  glColor3f(0,1,0);
  glVertex2f(1,1);
  glEnd();
  */

    { //background filling color
        // gradient color from top to bottom: white to light gray
        float alpha = (dragMode == MOVE || dragMode == RESIZE)? 0.7:1.0;
        glColor4f(fColor_back,fColor_back,fColor_back,1.0); //white

        glBegin(GL_QUADS);
        glColor4f(fColor_backTrans, fColor_backTrans, fColor_backTrans,alpha); // light gray
        glVertex2f(x  ,y-h);
        glVertex2f(x+w,y-h);

        //glColor4f(1,1,1,1.0); //white
        glColor4f(fColor_back,fColor_back,fColor_back,1.0); //white
        glVertex2f(x+w,y-titleHeight);
        glVertex2f(x  ,y-titleHeight);

        glEnd();

    }

    glBegin(GL_LINE_STRIP); //// rectangular contour of the window
    {
        //glColor4f(1,1,1,0.9);
        glColor4f(fColor_back,fColor_back,fColor_back,0.9);
        glVertex2f(x  ,y-h);
        glVertex2f(x  ,y  );
        glVertex2f(x+w,y  );

        //glColor4f(0.2,0.2,0.2,0.9);
        glColor4f(fColor_backTrans2,fColor_backTrans2,fColor_backTrans2,0.9);
        glVertex2f(x+w,y-h);
        glVertex2f(x  ,y-h);
    }
    glEnd();
}

void View::render() {
}

void View::setCreator(string c) {
    creator = c;
}

string View::getCreator() {
    return creator;
}

void View::setGraph(Graph *g) {
    assert(g);
    graph = g;
    //print("set Graph %s\n", g->getName().c_str());
}

void View::clearSelection(int selection) {
    if(!graph) return;
	NodeIterator *it = graph->createNodeIterator();
	for(it->begin();it->hasMoreElements();) {
		Node *node = (Node*) it->next();
        int sel = node->getPropertyInt("node.selected");
        if(sel==selection) node->setPropertyInt("node.selected",0);
    }
	graph->destroyIterator(it);
    controller->colorUpdated();
}

void View::addWindowClickable(Clickable *c) {
    windowClickables.push_back(c);
}
void View::addViewClickable(Clickable *c) {
    viewClickables.push_back(c);
}

/**** Generic Geometry Cache Support ****/
void View::invalidateGeometryCache() {
    validGeometryCache = false;
}

// TODO: Support timers. Function tic gets called when the timer expires
void View::tic() {
}

void View::deleteClickable(Clickable *c) {
    vector<Clickable*>::iterator p;
    p = find(viewClickables.begin(), viewClickables.end(), c);
    viewClickables.erase(p);
}


void View::textBounds(float &w, float &h, const char *text, const char *font, int fontsize) {
    int textx, texty, textw, texth;
    getBoundsText(textx,texty,textw,texth,(char*) text, font, fontsize);
    w = xpixels((float) textw);
    h = ypixels((float) texth);
}


/**
 * Get color associated with a given value in [0,1]
 */
void View::getColor(string colormap, float f, float *rgb) {
    controller->getColor(colormap, f, rgb);
}
/**
 * Get color indexed by integer i
 */
void View::getColor(string colormap, int i, float *rgb) {
    controller->getColor(colormap, i, rgb);
}


/**
 * Get min max of data property
 */
void View::getMinMax(string property, float &minv, float &maxv) {
    int type = controller->getPropertyType(property);
    minv = 0, maxv = 1.0;
    if(type==PROPERTY_FLOAT || type==PROPERTY_SIGNED) {
        graph->getStatManager()->getMinMaxFloat(property, minv, maxv);
    } else if(type==PROPERTY_INT) {
        graph->getStatManager()->getMinMaxFloat(property, minv, maxv);
    } else if(type==PROPERTY_BOOL) {
        maxv = 1.0; minv = 0.0;
    } else if(type==PROPERTY_STRING || type==PROPERTY_ORDINAL) {
        minv = 0;
        maxv = (float) controller->numStringValues(property);
    }
}

/**
 * Get min max of data property
 */
bool View::getMinMaxLog(string property, float &minlog, float &maxlog) {
	int type = controller->getPropertyType(property);
	if(type==PROPERTY_FLOAT || type==PROPERTY_SIGNED) {
		float minv, maxv;
		graph->getStatManager()->getMinMaxFloatPos(property, minv, maxv);
		if(maxv<1e-20) return false;
		if(minv<1e-7) minv = 1e-7;
		int l0 = (int) (log10(minv));
		while((float) pow(10,l0)>minv) l0--;
		int l1 = (int) (log10(maxv));
		while((float) pow(10,l1)<maxv) l1++;
		minlog = pow(10.0, (float) l0);
		maxlog = pow(10.0, (float) l1);
		return true;
	} else if(type==PROPERTY_INT) {
		return false;
	} else if(type==PROPERTY_BOOL) {
		return false;
	} else if(type==PROPERTY_STRING || type==PROPERTY_ORDINAL) {
		return false;
	}
	return false;
}

bool View::getMinMaxLog(vector<float> values, float &minlog, float &maxlog) {
	float minv = 1e20;
	float maxv = -1e20;
	for(unsigned int i=0;i<values.size();i++) {
		if(values[i]>0) {
			minv = fmin(minv, values[i]);
			maxv = fmax(maxv, values[i]);
		} 
	}
	int l0 = (int) (log10(minv));
	while((float) pow(10,l0)>minv) l0--;
	int l1 = (int) (log10(maxv));
	while((float) pow(10,l1)<maxv) l1++;
	minlog = pow(10.0, (float) l0);
	maxlog = pow(10.0, (float) l1);
}

/**
 * Prepare log axis of a property
 */
void View::getLogTics(string property, int numTics, vector<float> &tics, vector<string> &values) {
	tics.clear();
	values.clear();
	char str[200];
	int type = controller->getPropertyType(property);
	if(type==PROPERTY_FLOAT || type==PROPERTY_SIGNED) {
		float minv1, maxv1;
		getMinMaxLog(property, minv1, maxv1);
		float l0 = log10(minv1);
		float l1 = log10(maxv1);
		for(int i=0;i<=numTics;i++) {
			float x = (float) i/(float) numTics;
			float y = pow(10.0, l0 + x*(l1-l0));
			sprintf(str, "%.3f", y);
			tics.push_back(x);
			values.push_back(string(str));
		}
	}
}
/**
 * Prepare axis of a property
 */
void View::getTics(string property, int numTics, vector<float> &tics, vector<string> &values) {
	tics.clear();
	values.clear();
	char str[200];
    int type = controller->getPropertyType(property);
    if(type==PROPERTY_FLOAT || type==PROPERTY_SIGNED) {
		float minv1, maxv1;
		getMinMax(property, minv1, maxv1);
		for(int i=0;i<=numTics;i++) {
			float x = (float) i/(float) numTics;
			sprintf(str, "%.3f", minv1 + (maxv1-minv1)*x);
			tics.push_back(x);
			values.push_back(string(str));
		}
	}
	if(type==PROPERTY_INT) {
		float minv1, maxv1;
		getMinMax(property, minv1, maxv1);
		int interval = (int) (maxv1-minv1);
		int delta = max(1,(int)  ((float) interval/(float) (numTics)));
		for(int i=0;i<=interval;i+=delta) {
			float x = (float) i/(float) (interval);
			sprintf(str, "%d", (int) (i + minv1));
			tics.push_back(x);
			values.push_back(string(str));
		}
	}
	if(type==PROPERTY_STRING || type==PROPERTY_ORDINAL) {
		int numElements = (int) controller->numStringValues(property);
		int delta = max(1,(int)  ((float) numElements/(float) (numTics-1)));
		for(int i=0;i<numElements;i+=delta) {
			float x = (float) i/(float) (numElements-1);
			sprintf(str, "%s", controller->intToString(property,i).c_str());
			tics.push_back(x);
			values.push_back(string(str));
		}
	}
}
	

/**
 * Prepare log axis of a property
 */
void View::getLogTics(Interval interval, int numTics, vector<float> &tics, vector<string> &values) {
	tics.clear();
	values.clear();
	char str[200];
	float l0 = log10(interval.start);
	float l1 = log10(interval.end);
	for(int i=0;i<=numTics;i++) {
		float x = (float) i/(float) numTics;
		float y = pow(10.0, l0 + x*(l1-l0));
		sprintf(str, "%.3f", y);
		tics.push_back(x);
		values.push_back(string(str));
	}
}
/**
 * Prepare axis of a property
 */
void View::getTics(Interval interval, int numTics, vector<float> &tics, vector<string> &values) {
	tics.clear();
	values.clear();
	char str[200];
	for(int i=0;i<=numTics;i++) {
		float x = (float) i/(float) numTics;
		sprintf(str, "%.3f", interval.start + (interval.end-interval.start)*x);
		tics.push_back(x);
		values.push_back(string(str));
	}
}
/**
 * Get color associated with a data property
 */
void View::getAsColor(PropertyBag *bag, string property, string colormap, float *rgb) {
	if(colormap=="Default" || property=="Default") {
		rgb[0] = rgb[1] = rgb[2] = 0.0;
		return;
	}
	ColorMap *cmap = controller->getColormap(colormap);
	if(cmap && cmap->getType()==ColorMap::CONTINUOUS) {
			float f = graph->getAsFloat(bag, property, 0.0);
			getColor(colormap, f, rgb);
	} else {
			int i = graph->getAsInteger(bag, property, 0.0);
			getColor(colormap, i, rgb);
	}
}


/**
 * Get mapped property if it is a Virtual property or pass if it is not
 */

string View::PROPERTY(string property) {
    if(controller->isVirtualProperty(property)) {
        return controller->getVirtualProperty(property);
    }
    return property;
}

void View::getTextBounds(float &textwidth, float &textheight, const char * text) {
    // Todo: use the current font, as set by setFont(font, style, size);
    string font = currentFont;//getPropertyString("Font");
    int fontsize = currentFontSize;//getPropertyInt("Font Size", 1);

    float pixx, pixy;
    getPixelWindow(pixx, pixy);
    int textx, texty, textw, texth;
    getBoundsText(textx,texty,textw,texth, text, font.c_str(), fontsize);
    textwidth = (float) xpixels(textw);
    textheight = (float) ypixels(texth);
    //	toViewDisplacement(tw, th, textwidth, textheight);
}

void View::setFont(string font, int size, string style) {
    currentFont = font;
    currentFontSize = size;
    currentFontStyle = style;
}

void View::getTextBox(float &x, float &y, float &tw, float  &th, const char *text, string alignment, string alignmenty) {
    x = 0;
    y = 0;
    getTextBounds(tw, th, text);
    if(alignment=="center") {
        x = x - 0.5*tw;
    }
    if(alignment=="right") {
        x = x - 1.0*tw;
    }
    if(alignmenty=="middle") {
        y = y - 0.5*th;
    }
    if(alignmenty=="top") {
        y = y - 1.0*th;
    }
}

void View::renderTextBox(float x, float y, const char * text, string alignment, string alignmenty, float marginx, float marginy) {
    float tw, th;
    getTextBounds(tw, th, text);
    float xn ,yn;
    xn = x;
    yn = y;
    if(alignment=="center") {
        xn = x - 0.5*tw;
    }
    if(alignment=="right") {
        xn = x - 1.0*tw;
    }
    if(alignmenty=="middle") {
        yn = y - 0.5*th;
    }
    if(alignmenty=="top") {
        yn = y - 1.0*th;
    }

    glColor4f(1,1,1,1);
    glRectf(xn - marginx, yn - marginy, xn + tw + 1.0*marginx, yn + th + 1.0*marginy);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glColor4f(0,0,0,1);
    glRectf(xn - marginx, yn - marginy, xn + tw + 1.0*marginx, yn + th + 1.0*marginy);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    renderText(x, y, text, alignment, alignmenty);
}

void View::renderRoundTextBox(float x, float y, const char * text, string alignmentx, string alignmenty, float marginx, float marginy, float opacity) {
    float tx, ty, tw, th;
    float dx = marginx;
    float dy = marginy;
    getTextBox(tx, ty, tw, th, text, alignmentx, alignmenty);
    tw = (float) tw+dx;
    th = (float) th+dy;


    shadowRectangle(x + tx -dx/2 + 1.0*dx,
                    y + ty + ypixels(- 2.5) + 1.0*dy ,
                    tw - 2.0*dx,
                    th - 2.0*dy,
                    2.0*dx,
                    2.0*dy,
                    opacity*0.25);


    glColor4f(0.0,0.0,0.0,0.25*opacity);
    glColor4f(0.96,0.96,0.96,1.0*opacity);
    roundRectangle(x + tx - dx/2 + dx, y + ty -dy/2 + dy, fmax(0,tw - 2.0*dx), fmax(0,th - 2.0*dy), dx, dy);
    glColor4f(0,0,0,0.25*opacity);

    roundRectangleLine(x + tx -dx/2 + dx,
                       y + ty -dy/2 + dy,
                       tw - 2.0*dx,
                       th - 2.0*dy,
                       dx,
                       dy);

    glColor4f(0,0,0,1.0*opacity);
    renderText(x, y+dy, (char*) text, alignmentx, alignmenty); //0610 r33 yuhsuan change
    //renderText(x, y, (char*) text, alignmentx, alignmenty); // original Carlos'

}

void View::renderText(float x, float y, const char * text, string alignment, string alignmenty) {
    // Todo: use the current font
    int textx, texty, textw, texth;
    getBoundsText(textx,texty,textw,texth, text, currentFont.c_str(), currentFontSize);
    float textwidth = xpixels(textw), textheight = ypixels(texth);
    float xn, yn;
    xn = x;
    yn = y;
    if(alignment=="center") {
        xn = x - 0.5*textwidth;
    }
    if(alignment=="right") {
        xn = x - 1.0*textwidth;
    }
    if(alignmenty=="middle") {
        yn = y - 0.5*textheight;
    }
    if(alignmenty=="top") {
        yn = y - 1.0*textheight;
    }
    //glColor4f(0.5,0.5,0.5,0.5);
    //glRectf(xn, yn, xn+textwidth, yn+textheight);
    //glColor4f(0,0,0,1);
    drawText(xn, yn, text, currentFont.c_str(), currentFontSize, currentFontStyle);
}


void View::colorUpdated() {
}
void View::layoutUpdated() {
}

void View::updateFont(){
	string font = getPropertyString("View.Font");
	int fontsize = getPropertyInt("View.Font Size", 1);
	setFont(font, fontsize, "normal");
}

void View::selectElement(PropertyBag* element, int selectionId) {
	assert(element);
	bool changedSelection = false;
	if(element->getPropertyInt("node.selected")!=selectionId) {
		element->setPropertyInt("node.selected", selectionId);
		changedSelection = true;
	}
	if(changedSelection) controller->colorUpdated();
}

void View::selectElements(vector<PropertyBag *>&elements, int selectionId) {
	bool changedSelection = false;
	for(unsigned int i = 0;i<elements.size();i++) {
		PropertyBag *element = elements[i];
		if(element->getPropertyInt("node.selected")!=selectionId) {
			element->setPropertyInt("node.selected", selectionId);
			changedSelection = true;
		}
	}
	if(changedSelection) controller->colorUpdated();
}
